package com.retrozelda.nyanimals;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;

public class CRectangle implements INyanObject
{
	// ///////////////// OPENGL ///////////////////////////////
		// ////////////////////////////////////////////////////////
		// vertices
	protected static final int	FLOAT_SIZE_BYTES					= 4;
	protected static final int	TRIANGLE_VERTICES_DATA_STRIDE_BYTES	= 5 * FLOAT_SIZE_BYTES;
	protected static final int	TRIANGLE_VERTICES_DATA_POS_OFFSET	= 0;
	protected static final int	TRIANGLE_VERTICES_DATA_UV_OFFSET	= 3;
	protected final float[]		m_TriangleVerticesData				= {
				// X,	 Y, 	Z, 		U, 		V
				-1.0f,	-1.0f,	0.0f,	0.0f,	0.0f,	// 	right	top
				-1.0f, 	 1.0f,	0.0f,	0.0f,	1.0f,	// 	right	bottom
				 1.0f,	-1.0f,	0.0f, 	1.0f, 	0.0f,	// 	left	top
				 1.0f, 	 1.0f, 	0.0f, 	1.0f, 	1.0f};	//	left	bottom
																		

	protected FloatBuffer			m_TriangleVertices;

		// texture
		protected int							m_TextureID;
		protected int 						m_textureWidth;
		protected int 						m_textureHeight;

		// shader
		protected static final String m_VertexShader =	 
				  "uniform mat4 uMVPMatrix;\n"
				+ "attribute vec4 aPosition;\n"
				+ "attribute vec2 aTextureCoord;\n"
				+ "varying vec2 vTextureCoord;\n"
				+ "void main() {\n"
				+ "  gl_Position = uMVPMatrix * aPosition;\n"
				+ "  vTextureCoord = aTextureCoord;\n"
				+ "}\n";

		protected static final String m_FragmentShader_Tex =					
				  "precision mediump float;\n"
				+ "varying vec2 vTextureCoord;\n"
				+ "uniform sampler2D sTexture;\n"
				+ "void main() {\n"
				+ "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n"
				+ "}\n";
		protected static final String m_FragmentShader_Color =					
				  "precision mediump float;\n"
				+ "uniform vec4 vColor;\n"
				+ "void main() {\n"
				+ "  gl_FragColor = vColor;\n"
				//+ "  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
				+ "}\n";

		// shader handles
		protected int					m_ShaderProgram;
		protected int					m_MVPMatrixHandle;
		protected int					m_PositionHandle;
		protected int					m_TextureHandle;
		protected int					m_ColorHandle;
		
		// for solid color
		boolean 						m_solidColor;
		protected float					m_Red, m_Green, m_Blue, m_Alpha;

		// matrices
		protected float[]				m_MMatrix							= new float[16];		// model matrix
		protected float[]				m_MVPMatrix							= new float[16];		// model * view * proj
		protected float[]				m_position							= new float[2];
		protected float[]				m_scale								= new float[2];
		
		// ////////////////////////////////////////////////////////
		// ////////////////////////////////////////////////////////

		public float[] GetModelMatrix() { return m_MMatrix;}
		public float[] GetMVPMatrix() { return m_MVPMatrix;}
		public void SetModelMatrix(float[] model) { m_MMatrix = model;}
		public void SetMVPMatrix(float[] mvp) { m_MVPMatrix = mvp;}
		
	public CRectangle()
	{
		m_TriangleVertices = ByteBuffer.allocateDirect(m_TriangleVerticesData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
		m_TriangleVertices.put(m_TriangleVerticesData).position(0);
		SetPosition(0, 0);
		SetSize(1, 1);
	}
	
	public Object clone()
	{
		
		// TODO Auto-generated constructor stub
		CRectangle ret = null;
		try
		{
			ret = (CRectangle)super.clone();
			m_TriangleVertices = ret.m_TriangleVertices.duplicate();

			// texture
			m_TextureID = ret.m_TextureID;
			m_textureWidth = ret.m_textureWidth;
			m_textureHeight = ret.m_textureHeight;
			
			// shader handles
			m_ShaderProgram = ret.m_ShaderProgram;
			m_MVPMatrixHandle = ret.m_MVPMatrixHandle;
			m_PositionHandle = ret.m_PositionHandle;
			m_TextureHandle = ret.m_TextureHandle;
			m_ColorHandle = ret.m_ColorHandle;
					
			// for solid color
			m_solidColor = ret.m_solidColor;
			m_Red = ret.m_Red;
			m_Green = ret.m_Green;
			m_Blue = ret.m_Blue;
			m_Alpha = ret.m_Alpha;

			// matrices
			m_MMatrix = ret.m_MMatrix.clone();		// model matrix
			m_MVPMatrix = ret.m_MVPMatrix.clone();		// model * view * proj
			m_position = ret.m_position.clone();
			m_scale = ret.m_scale.clone();
		}
		catch (CloneNotSupportedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return (CRectangle)ret;		
	}
	
	public int[] getPosition()
	{
		int[] tempPos = new int[2];
		tempPos[0] = (int)m_position[0];
		tempPos[1] = (int)m_position[1];
		return tempPos;
	}
	
	public void SetPosition(int posX, int posY)
	{
		m_position[0] = (float)posX;
		m_position[1] = (float)posY;
	}
	
	public void SetSize(int Width, int Height)
	{
		m_scale[0] = (float)Width;
		m_scale[1] = (float)Height;
	}
	public int[] GetSize()
	{
		int[] scale = new int[2];
		scale[0] = (int)m_scale[0];
		scale[1] = (int)m_scale[1];
		
		return scale;
	}
	
	public int[] GetTextureSize()
	{
		int[] size = new int[2];
		size[0] = m_textureWidth;
		size[1] = m_textureHeight;
		
		return size;
	}

	private void LoadTextureShader(NyanimalGLSurfaceView game)
	{
		// create shader program
		m_ShaderProgram = CNyanimalGLRender.createProgram(m_VertexShader, m_FragmentShader_Tex);
		if (m_ShaderProgram == 0)
		{
			return;
		}
	}
	private void LoadColorShader(NyanimalGLSurfaceView game)
	{
		// create shader program
		m_ShaderProgram = CNyanimalGLRender.createProgram(m_VertexShader, m_FragmentShader_Color);
		if (m_ShaderProgram == 0)
		{
			return;
		}
	}
	

	// RGBA is value between 0 and 255
	public void Init(NyanimalGLSurfaceView game, int R, int G, int B, int A)
	{
		// get the 0 - 255 to be 0.0 - 1.0
		m_Red = ((float)R / 255.0f);
		m_Green = ((float)G / 255.0f);
		m_Blue = ((float)B / 255.0f);
		m_Alpha = ((float)A / 255.0f);
		
		m_solidColor = true;
		LoadColorShader(game);
		FinishInit(game);
	}

	@Override
	public void Init(NyanimalGLSurfaceView game)
	{
		m_solidColor = false;
		LoadTextureShader(game);
		FinishInit(game);
	}

	private void FinishInit(NyanimalGLSurfaceView game)
	{
		// get positional handle
		m_PositionHandle = GLES20.glGetAttribLocation(m_ShaderProgram, "aPosition");
		if (m_PositionHandle == -1)
		{
			throw new RuntimeException("Could not get attrib location for aPosition");
		}

		if(m_solidColor == true)
		{
			// get Color handle
			if(m_solidColor == true)
			{
				m_ColorHandle = GLES20.glGetUniformLocation(m_ShaderProgram, "vColor");
				if (m_ColorHandle == -1)
				{
					throw new RuntimeException("Could not get attrib location for vColor");
				}
			}
		}
		else
		{
			// get texture handle
			m_TextureHandle = GLES20.glGetAttribLocation(m_ShaderProgram, "aTextureCoord");
			if (m_TextureHandle == -1)
			{
				throw new RuntimeException("Could not get attrib location for aTextureCoord");
			}
		}
		// get the mvp matrix handle
		m_MVPMatrixHandle = GLES20.glGetUniformLocation(m_ShaderProgram, "uMVPMatrix");
		if (m_MVPMatrixHandle == -1)
		{
			throw new RuntimeException("Could not get attrib location for uMVPMatrix");
		}
		
				
		// default the MVP
		Matrix.setIdentityM(m_MVPMatrix, 0);
	}
	
	@Override
	public void Update(float deltaTime)
	{
		SetSize(m_textureWidth, m_textureHeight);
	}
	
	@Override
	public void Draw(CArgumentGroup arg)
	{

		
		// NOTE: can do "event based"-like without setting to identity each frame
		////////////////////////////////////////
		// freshen the model matrix
		Matrix.setIdentityM(m_MMatrix, 0);
		
		// set the position
		Matrix.translateM(m_MMatrix, 0, m_position[0], m_position[1], 0.0f);
		
		// set any rotations
		
		// set any scales
		Matrix.scaleM(m_MMatrix, 0, m_scale[0], m_scale[1], 1.0f);
		////////////////////////////////////////
		
		// calculate the MVP
		Matrix.multiplyMM(m_MVPMatrix, 0, arg.GameClass.GetViewProjection(), 0, m_MMatrix, 0);
		
		
		// activate the stuff
		GLES20.glUseProgram(m_ShaderProgram);
		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_TextureID);

		// set the verts
		m_TriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
		GLES20.glVertexAttribPointer(m_PositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, m_TriangleVertices);

		// set the UVs
		m_TriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
		GLES20.glEnableVertexAttribArray(m_PositionHandle);

		// set the texture
		GLES20.glVertexAttribPointer(m_TextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, m_TriangleVertices);
		GLES20.glEnableVertexAttribArray(m_TextureHandle);
		
		// set the MVP
		GLES20.glUniformMatrix4fv(m_MVPMatrixHandle, 1, false, m_MVPMatrix, 0);

		// set teh color
		if(m_solidColor == true)
		{
			GLES20.glUniform4f(m_ColorHandle, m_Red, m_Green, m_Blue, m_Alpha );
		}
		
		// Draw
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

	}
	
	public void LoadTexture(int resource, NyanimalGLSurfaceView game)
	{
		// create the texture
		// NOTE: textures need to be done each time the main surface is created
		int[] textures = new int[1];
		GLES20.glGenTextures(1, textures, 0);

		// bind the texture
		m_TextureID = textures[0];
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_TextureID);

		// set the mip and mag
		GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
		GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

		// set the wrap mode
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

		// load the texture image
		InputStream is = game.getContext().getResources() .openRawResource(resource);
		Bitmap bitmap;
		try
		{
			bitmap = BitmapFactory.decodeStream(is);
			m_textureWidth = bitmap.getWidth();
			m_textureHeight = bitmap.getHeight();
		}
		finally
		{
			try
			{
				is.close();
			}
			catch (IOException e)
			{
				// Ignore.
			}
		}

		MakeTextureSize();

		// free the texture
		GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
		bitmap.recycle();
	}
	public void MakeTextureSize()
	{
		SetSize(m_textureWidth, m_textureHeight);
	}
    
}












